﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Text;
using System.Threading.Tasks;

namespace DbFrame.Class
{
    public static class ExpressionTreeHelper
    {

        /// <summary>
        /// 表达式计算
        /// </summary>
        /// <param name="exp"></param>
        /// <returns></returns>
        public static void DealExpress(Expression exp, ParserArgs args)
        {
            if (exp is LambdaExpression)
            {
                DealExpress((exp as LambdaExpression).Body, args);
            }
            else if (exp is BinaryExpression)
            {
                DealBinaryExpression(exp as BinaryExpression, args);
            }
            else if (exp is MemberExpression)
            {
                var me = exp as MemberExpression;
                if (me.Expression is ParameterExpression)
                {
                    if (args.TabIsAlias) DealExpress(me.Expression, args);
                    args.Builder.Append(me.Member.Name);
                }
                else
                {
                    if (me != null)
                    {
                        var typeName = me.Expression.GetType().Name;
                        if (typeName == "TypedParameterExpression")
                            args.Builder.Append(me.Member.Name);
                        else
                            args.AddParameter(Eval(exp));
                    }
                }

            }
            else if (exp is ConstantExpression)
            {
                DealConstantExpression(exp as ConstantExpression, args);
            }
            else if (exp is UnaryExpression)
            {
                DealUnaryExpression(exp as UnaryExpression, args);
            }
            else if (exp is ParameterExpression)
            {
                args.Builder.Append((exp as ParameterExpression).Name + ".");
            }
            else if (exp is NewArrayExpression)
            {
                var member = exp as NewArrayExpression;
                StringBuilder tmpstr = new StringBuilder();
                foreach (Expression ex in member.Expressions)
                {
                    tmpstr.Append("'" + Eval(ex) + "'");
                    tmpstr.Append(",");
                }
                args.AddParameter(tmpstr.ToString(0, tmpstr.Length - 1));
            }
            else if (exp is MethodCallExpression)
            {
                var member = exp as MethodCallExpression;
                if (member.Arguments.Count > 0)
                    DealMethodCall(member, args);
                else
                    args.AddParameter(Eval(member));
            }
            else
            {
                throw new NotImplementedException("无法解析的表达式！");
            }
        }

        private static void DealMethodCall(MethodCallExpression ex, ParserArgs args)
        {
            dynamic name = ex.Object;
            switch (ex.Method.Name)
            {
                case "StartsWith":
                    args.Builder.Append(name.Member.Name + " LIKE ");
                    DealExpress(ex.Arguments[0], args);
                    args.Builder.Append(" + '%' ");
                    break;
                case "Contains":
                    args.Builder.Append(name.Member.Name + " LIKE '%' + ");
                    DealExpress(ex.Arguments[0], args);
                    args.Builder.Append(" + '%' ");
                    break;
                case "EndsWith":
                    args.Builder.Append(name.Member.Name + " LIKE '%' + ");
                    DealExpress(ex.Arguments[0], args);
                    break;
                case "Equals":
                    args.Builder.Append(name.Member.Name + " = ");
                    DealExpress(ex.Arguments[0], args);
                    break;


                case "Like":
                    args.Builder.Append((ex.Arguments[0] as MemberExpression).Member.Name + " LIKE ");
                    DealExpress(ex.Arguments[1], args);
                    break;
                case "In":
                    args.Builder.Append((ex.Arguments[0] as MemberExpression).Member.Name + "  IN ( ");
                    DealExpress(ex.Arguments[1], args);
                    args.Builder.Append(" ) ");
                    break;
                case "NotIn":
                    args.Builder.Append((ex.Arguments[0] as MemberExpression).Member.Name + " NOT IN ( ");
                    DealExpress(ex.Arguments[1], args);
                    args.Builder.Append(" ) ");
                    break;
                default:
                    args.AddParameter(Eval(ex));
                    break;
            }
        }

        private static void DealUnaryExpression(UnaryExpression exp, ParserArgs args)
        {
            DealExpress(exp.Operand, args);
        }

        private static void DealBinaryExpression(BinaryExpression exp, ParserArgs args)
        {
            DealExpress(exp.Left, args);
            args.Builder.Append(GetOperStr(exp.NodeType));
            DealExpress(exp.Right, args);
        }

        private static void DealConstantExpression(ConstantExpression exp, ParserArgs args)
        {
            args.AddParameter(exp.Value);
        }

        private static string GetOperStr(ExpressionType type)
        {
            switch (type)
            {
                case ExpressionType.OrElse: return " OR ";
                case ExpressionType.Or: return " | ";
                case ExpressionType.AndAlso: return " AND ";
                case ExpressionType.And: return " & ";
                case ExpressionType.GreaterThan: return " > ";
                case ExpressionType.GreaterThanOrEqual: return " >= ";
                case ExpressionType.LessThan: return " < ";
                case ExpressionType.LessThanOrEqual: return " <= ";
                case ExpressionType.NotEqual: return " <> ";
                case ExpressionType.Add: return " + ";
                case ExpressionType.Subtract: return " - ";
                case ExpressionType.Multiply: return " * ";
                case ExpressionType.Divide: return " / ";
                case ExpressionType.Modulo: return " % ";
                case ExpressionType.Equal: return " = ";
                default: throw new NotImplementedException("无法解析的表达式！");
            }
        }

        /// <summary>
        /// 计算值
        /// </summary>
        /// <param name="expression"></param>
        /// <returns></returns>
        public static object Eval(Expression expression)
        {
            UnaryExpression cast = Expression.Convert(expression, typeof(object));
            var obj = Expression.Lambda<Func<object>>(cast).Compile().Invoke();
            return obj != null ? GetValueFormat(obj) : obj;
        }

        /// <summary>
        /// 对某些值 特殊处理
        /// </summary>
        /// <param name="obj"></param>
        private static object GetValueFormat(object obj)
        {
            var type = obj.GetType();
            if (type.Name == "List`1") //list集合
            {
                List<string> data = new List<string>();
                var list = obj as IEnumerable;
                string sql = string.Empty;
                foreach (var item in list)
                {
                    data.Add(GetValueFormat(item).To_String());
                }
                sql = string.Join(",", data);
                return sql;
            }
            return obj;
        }

    }
}
